/*
 * @Author: BuddyCoder
 * @Date: 2021-08-24 14:50:48
 * @LastEditors: BuddyCoder
 * @LastEditTime: 2023-03-16 10:17:32
 * @Description:
 * @FilePath: /pgmacro/src/macros/crud_table_impl.rs
 * MIT
 */
//use proc_macro2::Ident;
use quote::*;
use syn::Token;

//驼峰转换成下划线
fn to_snake_name(name: &str) -> String {
    let chs = name.chars();
    let mut new_name = String::new();
    let mut index = 0;
    let chs_len = name.len();
    for x in chs {
        if x.is_uppercase() {
            if index != 0 && (index + 1) != chs_len {
                new_name.push_str("_");
            }
            new_name.push_str(x.to_lowercase().to_string().as_str());
        } else {
            new_name.push(x);
        }
        index += 1;
    }
    return new_name;
}

//下划线下划线驼峰
fn to_hump(name: &str) -> String {
    let binding = name.to_string();
    let chslist: Vec<&str> = binding.split('_').collect();
    let mut new_name = String::new();
    for dc in chslist {
        let chs = dc.chars();
        let mut index = 0;
        for x in chs {
            if index == 0 {
                new_name.push_str(x.to_uppercase().to_string().as_str());
            } else {
                new_name.push(x);
            }
            index += 1;
        }
    }
    return new_name;
}

struct FieldExt {
    //   struct FieldExt<'a> {
    // ty: &'a syn::Type,
    //存储类型
    ident: syn::Ident,
    // 结构体信息
    //  named: bool,
    //是否是有名字的结构体
    //  optioned:bool
    //是否是Option<>
}

//impl<'a> FieldExt<'a> {
impl<'a> FieldExt {
    //实例化一个对象
    pub fn new(field: &'a syn::Field, idx: usize, named: bool) -> FieldExt {
        FieldExt {
            // ty: &field.ty,
            ident: if named {
                field.ident.clone().unwrap()
            } else {
                syn::Ident::new(&format!("f{}", idx), proc_macro2::Span::call_site())
            },
            //  named: named,
            //  optioned:regex_options(field.ty.to_token_stream().to_string()),
        }
    }
    //生成新增的字段字符串拼接代码
    fn py_insert_column(&self) -> proc_macro2::TokenStream {
        let field_name = &self.ident;
        let field_name_str = format!("\"{}\"", field_name);
        quote! {
            // The generated impl.
            match self.#field_name {
                Some(_) => {
                    sql_columns.push(#field_name_str);
                    agrs.push(&self.#field_name);
                }
                None => (),
            }
        }
    }
    //生成更新的字段字符串拼接代码
    fn py_update_column(&self) -> proc_macro2::TokenStream {
        let field_name = &self.ident;
        let field_name_str = format!("\"{}\"", field_name);
        quote! {
            match self.#field_name {
                Some(_) => {
                    //let column = #field_name_str + "=${}".to_string() + agrs_number.to_string();
                    let column = format!("{}=${}", #field_name_str,agrs_number);
                    sql_columns.push(column);
                    agrs.push(&self.#field_name);
                    agrs_number = agrs_number + 1;
                }
                None => (),
            }
        }
    }
    //生成枚举column的代码
    fn py_enum_column(&self) -> proc_macro2::TokenStream {
        let field_name = &self.ident;
        let field_name_str = format!("{}", field_name);
        let field_name_to_hump = to_hump(field_name_str.as_str());
        let field_name_to_ident =
            syn::Ident::new(&field_name_to_hump, proc_macro2::Span::call_site());
        quote! {
            #field_name_to_ident,
        }
    }

    fn py_enum_column_impl(&self, enum_name: &syn::Ident) -> proc_macro2::TokenStream {
        // let enum_name = format!("{}Column",struct_name);
        // let enum_name_ident = syn::Ident::new(&enum_name, proc_macro2::Span::call_site());
        let field_name = &self.ident;
        let field_name_str = format!("{}", field_name);
        let field_name_to_hump = to_hump(field_name_str.as_str());
        let field_name_to_ident =
            syn::Ident::new(&field_name_to_hump, proc_macro2::Span::call_site());
        // let field_name_re = format!("{}", field_name);
        quote! {
            #enum_name::#field_name_to_ident => #field_name_str ,
        }
    }

    fn py_row_column(&self) -> proc_macro2::TokenStream {
        let field_name = &self.ident;
        let row_name = format!("{}", field_name);
        quote! {
            #field_name: row.try_get(#row_name).unwrap_or_default(),
        }
    }

    fn py_field_list_name(&self) -> String {
        let field_name = &self.ident;
        format!("\"{}\"", field_name)
    }
}

//根据枚举类型syn::Fields，调用new_impl() ,分别传入不同的参数
pub fn impl_crud_driver(
    derive_input: &syn::DeriveInput,
    fields: &syn::Fields,
) -> proc_macro2::TokenStream {
    match *fields {
        syn::Fields::Named(ref fields) => new_impl(&derive_input, Some(&fields.named), true),
        syn::Fields::Unit => new_impl(&derive_input, None, false),
        syn::Fields::Unnamed(ref fields) => new_impl(&derive_input, Some(&fields.unnamed), false),
    }
}

fn py_get_name(table_name: &str) -> proc_macro2::TokenStream {
    quote! {
        fn get_table_name(prefix: Option<String>, suffix:Option<String>) -> String {
           let temp_table_name = match prefix {
                Some(t) => format!("{}_{}",t,#table_name),
                None => format!("{}",#table_name)
            };
           match  suffix {
               Some(t)=> format!("{}_{}",temp_table_name,t),
               None => format!("{}",temp_table_name)
           }
        }
    }
}

//主要的实现函数
//主要的实现函数
/**
 * @description:
 * @param {&syn::DeriveInput} ast
 * @param {Option<&syn::punctuated::Punctuated<syn::Field, Token![,]>>} fields
 * @param {bool} named
 * @return {*}
 */
fn new_impl(
    ast: &syn::DeriveInput,
    fields: Option<&syn::punctuated::Punctuated<syn::Field, Token![,]>>,
    named: bool,
) -> proc_macro2::TokenStream {
    let struct_name = &ast.ident; //结构体的名字
                                  //let unit = fields.is_none(); //结构体是否为空
    let empty = Default::default(); //这句话没看明白，先放着
    let fields: Vec<_> = fields
        .unwrap_or(&empty) //把值提取出来
        .iter() //对提取出来字段信息进行迭代器遍历
        .enumerate() //可以使用enumerate()方法，该方法在每次迭代中返回一个包含索引和项目的元组：
        .map(|(i, f)| FieldExt::new(f, i, named))
        .collect();

    let table_name = to_snake_name(&struct_name.to_string());
    println!("开始编译  PGCRUD:{}", table_name);
    //编译获取表名称
    let py_get_name = py_get_name(table_name.as_str());

    //自建名称列表
    let py_field_list: Vec<String> = fields.iter().map(|f| f.py_field_list_name()).collect();
    let field_list_str = py_field_list.join(",");
    let py_get_field_list = quote! {
        fn get_field_list() -> String{
            #field_list_str.to_string()
        }
    };

    //===========================================================================================================
    let py_insert_quoted = fields.iter().map(|f| f.py_insert_column());
    let py_insert = quote! {
        fn gen_save(&self,prefix:Option<String>,suffix:Option<String>) -> Result<(String, Vec<&(dyn ToSql + Sync)>), String> {
            let table_name = Self::get_table_name(prefix,suffix);
            let mut sql_columns: Vec<&str> = Vec::new();
            let mut sql_agrs: Vec<String> = Vec::new();
            let mut agrs: Vec<&(dyn ToSql + Sync)> = Vec::new();
            #(#py_insert_quoted)*
            if agrs.len() < 1 {
                return Err("未传入任何有效值".to_string());
            }

            for i in 0..sql_columns.len() {
                let agr = format!("${}", i + 1);
                sql_agrs.push(agr)
            }

            let sql = format!(
                "INSERT INTO {} ( {} ) VALUES( {} )",
                table_name,
                sql_columns.join(","),
                sql_agrs.join(",")
            );
            return Ok((sql, agrs));
        }
    };

    //=======================================================================================================
    let py_update_quoted = fields.iter().map(|f| f.py_update_column());
    let py_update = quote! {
            fn gen_update(&self,prefix:Option<String>,suffix:Option<String>) -> Result<(String, Vec<&(dyn ToSql + Sync)>, u32), String> {
                let table_name = Self::get_table_name(prefix,suffix);
                let mut sql_columns: Vec<String> = Vec::new();
                // let mut sql_agrs: Vec<String> = Vec::new();
                let mut agrs: Vec<&(dyn ToSql + Sync)> = Vec::new();
                let mut agrs_number = 1;
                #(#py_update_quoted)*
                let sql = format!("UPDATE {}  SET  {}  ", table_name, sql_columns.join(","),);
                return Ok((sql, agrs, agrs_number));
            }
    };
    //=======================================================================================================
    let py_row_column_quoted = fields.iter().map(|f| f.py_row_column());

    let py_return_one_quoted = quote! {
        fn return_one(row: Row) -> Self {
            #struct_name {
                #(#py_row_column_quoted)*
            }
        }
    };

    //=============================================================================================================
    //枚举的字符段名称
    let py_enum_column_quoted = fields.iter().map(|f| f.py_enum_column());
    let enum_name = syn::Ident::new(
        &format!("{}Column", struct_name),
        proc_macro2::Span::call_site(),
    );
    //syn::Ident::new(&format!("f{}", idx), proc_macro2::Span::call_site())
    let py_enum_column = quote! {
        pub enum  #enum_name {
            #(#py_enum_column_quoted)*
        }
    };
    //=============================================================================================================
    //枚举的字符段的实现
    let py_enum_column_impl_quoted = fields.iter().map(|f| f.py_enum_column_impl(&enum_name));
    let py_enum_column_impl = quote! {
        impl ColumnExt for #enum_name {
            fn get(&self)->&'static str{
                match self {
                    #(#py_enum_column_impl_quoted)*
                }
            }
        }
    };

    //=============================================================================================================
    quote!(
        #py_enum_column

        #py_enum_column_impl

        impl Parameters for #struct_name {
          #py_get_name
          #py_get_field_list
          #py_insert
          #py_update
          #py_return_one_quoted
        }
    )
}
